﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ActionSystem : PersistentSingleton<ActionSystem>
{
    // 当前处理的反应栈，用栈支持嵌套
    private Stack<List<GameAction>> reactionStack = new();

    // 只读表示是否正在执行
    public bool IsPerforming { get; private set; } = false;

    // 订阅字典
    private static Dictionary<Type, List<Action<GameAction>>> preSubs = new();
    private static Dictionary<Type, List<Action<GameAction>>> postSubs = new();
    private static Dictionary<Type, Func<GameAction, IEnumerator>> performers = new();

    // 请求队列，串行执行
    private Queue<(GameAction action, Action callback)> actionQueue = new();

    #region Perform / Queue
    public void Perform(GameAction action, Action OnPerformFinshed = null)
    {
        actionQueue.Enqueue((action, OnPerformFinshed));
        if (!IsPerforming)
        {
            StartCoroutine(ProcessQueue());
        }
    }

    private IEnumerator ProcessQueue()
    {
        IsPerforming = true;
        while (actionQueue.Count > 0)
        {
            var (action, callback) = actionQueue.Dequeue();
            yield return Flow(action, () =>
            {
                try
                {
                    callback?.Invoke();
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            });
        }
        IsPerforming = false;
    }
    #endregion

    // AddReaction 放入当前正在处理的反应列表栈顶，如果没有则直接 Perform
    public void AddReaction(GameAction action)
    {
        if (reactionStack.Count > 0)
        {
            reactionStack.Peek().Add(action);
        }
        else
        {
            Perform(action);
        }
    }

    private IEnumerator Flow(GameAction action, Action OnFlowFlished = null)
    {
        // PRE
        yield return RunPhase(action.PreReactions, action, preSubs, "PRE");

        // PERFORMER
        yield return RunPerformer(action);

        // POST
        yield return RunPhase(action.PostReactions, action, postSubs, "POST");

        OnFlowFlished?.Invoke();
    }

    private IEnumerator RunPhase(List<GameAction> reactionsList, GameAction action, Dictionary<Type, List<Action<GameAction>>> subs, string phaseName)
    {
        if (reactionsList == null) reactionsList = new List<GameAction>();

        // 压入当前反应栈，支持嵌套
        reactionStack.Push(reactionsList);

        PerformSubscribers(action, subs);

        yield return PerformReactions();

        reactionStack.Pop();
    }

    private IEnumerator RunPerformer(GameAction action)
    {
        Type type = action.GetType();
        if (performers.ContainsKey(type))
        {
            yield return performers[type](action);
        }
    }

    private void PerformSubscribers(GameAction action, Dictionary<Type, List<Action<GameAction>>> subs)
    {
        Type type = action.GetType();
        if (subs.ContainsKey(type))
        {
            // 拷贝数组，防止回调修改列表导致异常
            var subscribers = subs[type].ToArray();
            foreach (var sub in subscribers)
            {
                try
                {
                    sub(action);
                }
                catch (Exception ex)
                {
                    Debug.LogException(ex);
                }
            }
        }
    }

    private IEnumerator PerformReactions()
    {
        if (reactionStack.Count == 0) yield break;

        var currentList = reactionStack.Peek();

        while (currentList.Count > 0)
        {
            var next = currentList[0];
            currentList.RemoveAt(0);
            yield return Flow(next);
        }
    }

    #region 注册/注销 Performer & Reaction Subscriber
    public static void AttachPerformer<T>(Func<T, IEnumerator> performer) where T : GameAction
    {
        Type type = typeof(T);
        IEnumerator wrappedPerformer(GameAction action) => performer((T)action);
        if (performers.ContainsKey(type)) performers[type] = wrappedPerformer;
        else performers.Add(type, wrappedPerformer);
    }

    public static void DetachPerformer<T>() where T : GameAction
    {
        Type type = typeof(T);
        if (performers.ContainsKey(type)) performers.Remove(type);
    }

    private static Dictionary<Delegate, Action<GameAction>> reactionMap = new();

    public static void SubscribeReaction<T>(Action<T> reaction, ReactionTiming timing) where T : GameAction
    {
        Dictionary<Type, List<Action<GameAction>>> subs = timing == ReactionTiming.PRE ? preSubs : postSubs;
        Type type = typeof(T);

        if (!subs.ContainsKey(type))
            subs[type] = new List<Action<GameAction>>();

        Action<GameAction> wrappedReaction = (GameAction action) => reaction((T)action);
        reactionMap[reaction] = wrappedReaction;
        subs[type].Add(wrappedReaction);
    }

    public static void UnsubscribeReaction<T>(Action<T> reaction, ReactionTiming timing) where T : GameAction
    {
        Dictionary<Type, List<Action<GameAction>>> subs = timing == ReactionTiming.PRE ? preSubs : postSubs;
        Type type = typeof(T);

        if (subs.ContainsKey(type) && reactionMap.TryGetValue(reaction, out Action<GameAction> wrappedReaction))
        {
            subs[type].Remove(wrappedReaction);
            reactionMap.Remove(reaction);
        }
    }
    #endregion
}
 